/*****************************************************************************
*
*  PROJECT:     Native Executive
*  LICENSE:     See LICENSE in the top level directory
*  FILE:        NativeExecutive/CExecutiveManager.cond.cpp
*  PURPOSE:     Hazard-safe conditional variable implementation
*  DEVELOPERS:  Martin Turski <wordwhirl@outlook.de>
*
*  Multi Theft Auto is available from http://www.multitheftauto.com/
*
*****************************************************************************/

#include "StdInc.h"

#include "CExecutiveManager.cond.h"

#include "internal/CExecutiveManager.cond.internal.h"

BEGIN_NATIVE_EXECUTIVE

condNativeEnvRegister_t condNativeEnvRegister;

void condVarNativeEnv::condVarThreadPlugin::Initialize( CExecThreadImpl *thread )
{
    CExecutiveManagerNative *manager = thread->manager;

    // We initially do not wait on any condition variable.
    this->waitingOnVar = NULL;
}

void condVarNativeEnv::condVarThreadPlugin::Shutdown( CExecThreadImpl *thread )
{
    // Make sure we are not waiting on any condVar anymore.
    // This is guaranteed by the thread logic and hazard management system.
    assert( this->waitingOnVar == NULL );
}

void condVarNativeEnv::condVarThreadPlugin::TerminateHazard( void )
{
    // If we get here then the hazard it correctly initialized.
    // This is made secure because we take the threadState lock in the Wait method.

    // Wake the thread.
    // The mechanism of the conditional variable will make sure he cannot get into
    // waiting state again.
    CCondVarImpl *waitingOnVar = this->waitingOnVar;

    assert( waitingOnVar != NULL );

    waitingOnVar->condVarInternal.notify_all();
}

CCondVarImpl::CCondVarImpl( CExecutiveManagerNative *manager )
{
    this->manager = manager;
}

CCondVarImpl::~CCondVarImpl( void )
{
    // TODO: wake up all threads that could be waiting at this conditional variable.
}

void CCondVarImpl::Wait( CReadWriteWriteContextSafe <>& ctxLock )
{
    CExecutiveManagerNative *nativeMan = this->manager;

    condVarNativeEnv *condEnv = condNativeEnvRegister.GetPluginStruct( nativeMan );

    assert( condEnv != NULL );

    CExecThreadImpl *nativeThread = (CExecThreadImpl*)nativeMan->GetCurrentThread();

    condVarNativeEnv::condVarThreadPlugin *threadCondEnv = condEnv->GetThreadCondEnv( nativeThread );

    assert( threadCondEnv != NULL );

    // We must not let the thread switch from RUNNING into TERMINATING state here.
    // * if the thread is RUNNING then we can make him wait.
    // * if the thread is TERMINATING then we must throw an exception to kill it.

    CReadWriteWriteContextSafe <> ctxThreadState( nativeThread->lockThreadStatus );

    // Only problem could be termination request, since a wait would obstruct it.
    nativeThread->CheckTerminationRequest();

    // FROM HERE ON, we cannot prematurely trigger hazard term request.

    // We need to know what conditional variable we wait on.
    threadCondEnv->waitingOnVar = this;

    PushHazard( nativeMan, threadCondEnv );

    try
    {
        // Wait for stuff.
        {
            struct rtlock_handler
            {
                AINLINE rtlock_handler( CReadWriteWriteContextSafe <>& lockCtx, CReadWriteWriteContextSafe <>& lockUserCtx ) : lockCtx( lockCtx ), lockUserCtx( lockUserCtx )
                {
                    this->lockThreadStatus = lockCtx.GetCurrentLock();
                    this->lockUserContext = lockUserCtx.GetCurrentLock();
                }

                AINLINE void lock( void )
                {
                    lockCtx = CReadWriteWriteContextSafe <> ( lockThreadStatus );
                    lockUserCtx = CReadWriteWriteContextSafe <> ( lockUserContext );
                }

                AINLINE void unlock( void )
                {
                    lockCtx.Suspend();
                    lockUserCtx.Suspend();
                }

                CReadWriteLock *lockThreadStatus;
                CReadWriteLock *lockUserContext;
                CReadWriteWriteContextSafe <>& lockCtx;
                CReadWriteWriteContextSafe <>& lockUserCtx;
            };

            rtlock_handler l_ctxHazardInit( ctxThreadState, ctxLock );

            this->condVarInternal.wait( l_ctxHazardInit );
        }
    }
    catch( ... )
    {
        PopHazard( nativeMan );

        throw;
    }

    // Remove our hazard again.
    PopHazard( nativeMan );

    // Not waiting on any thread anymore.
    threadCondEnv->waitingOnVar = NULL;

    // We could have woken up by hazard-check, in which case we probably are asked to terminate.
    nativeThread->CheckTerminationRequest();
}

void CCondVarImpl::Signal( void )
{
    this->condVarInternal.notify_all();
}

void CCondVar::Wait( CReadWriteWriteContextSafe <>& ctxLock )   { ((CCondVarImpl*)this)->Wait( ctxLock ); }
void CCondVar::Signal( void )                                   { ((CCondVarImpl*)this)->Signal(); }

CCondVar* CExecutiveManager::CreateConditionVariable( void )
{
    CExecutiveManagerNative *nativeMan = (CExecutiveManagerNative*)this;

    CCondVarImpl *condVar = new CCondVarImpl( nativeMan );

    return condVar;
}

void CExecutiveManager::CloseConditionVariable( CCondVar *condVar )
{
    CExecutiveManagerNative *nativeMan = (CExecutiveManagerNative*)this;

    CCondVarImpl *nativeCondVar = (CCondVarImpl*)condVar;

    delete nativeCondVar;
}

void registerConditionalVariables( void )
{
    condNativeEnvRegister.RegisterPlugin( executiveManagerFactory );
}

END_NATIVE_EXECUTIVE